﻿// 2025/10/15: 首个版本
// 2025/11/12: 优化tab control显示

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Avalonia;
using Avalonia.Controls;
using ASEva;
using ASEva.UIAvalonia;
using ASEva.Utility;

namespace CommonSource
{
    partial class PluginLibraryDialog : DialogPanel
    {
        public PluginLibraryDialog() // For designer
        {
            InitializeComponent();
            language = new LanguageSwitch(Resources, AgencyLocal.GetAppLanguage());
        }

        public PluginLibraryDialog(object caller, String iconResourceName = null)
        {
            InitializeComponent();
            SetResizableMode(600, 400, 910, 610);
            language = new LanguageSwitch(Resources, AgencyLocal.GetAppLanguage());
            Icon = ResourceLoader.Load(iconResourceName)?.ToAvaloniaWindowIcon();
            Title = language["title"];

            if (AgencyLocal.ClientSide)
            {
                if (caller != null)
                {
                    localModel = dualPanelLocal.Initialize(caller, PluginLibraryDialogTabViewModel.Mode.ClientLocal);
                    remoteModel = dualPanelRemote.Initialize(caller, PluginLibraryDialogTabViewModel.Mode.HostRemote);
                    tabControl.IsVisible = true;
                }
                else
                {
                    localModel = singlePanel.Initialize(caller, PluginLibraryDialogTabViewModel.Mode.ClientLocal);
                    singlePanel.IsVisible = true;
                }
            }
            else
            {
                localModel = singlePanel.Initialize(caller, PluginLibraryDialogTabViewModel.Mode.HostLocal);
                singlePanel.IsVisible = true;
            }
        }

        public bool LocalModified => localModel.Modified;
        public bool RemoteModified => remoteModel?.Modified ?? false;

        private LanguageSwitch language;
        private PluginLibraryDialogTabViewModel localModel;
        private PluginLibraryDialogTabViewModel remoteModel;
    }

    partial class PluginLibraryDialogTab : Panel
    {
        public PluginLibraryDialogTab()
        {
            InitializeComponent();
            language = new LanguageSwitch(Resources, AgencyLocal.GetAppLanguage());
        }

        public PluginLibraryDialogTabViewModel Initialize(object caller, PluginLibraryDialogTabViewModel.Mode mode)
        {
            if (mode == PluginLibraryDialogTabViewModel.Mode.ClientLocal)
            {
                gridDetails.RowDefinitions = new RowDefinitions("*");
                gridNativeDetails.IsVisible = false;
            }

            var model = new PluginLibraryDialogTabViewModel(this,language, caller, mode);
            model.InitializeList();
            DataContext = model;
            return model;
        }

        private LanguageSwitch language;
    }

    partial class PluginLibraryDialogTabViewModel(Control view, LanguageSwitch language, object caller, PluginLibraryDialogTabViewModel.Mode mode) : ObservableObject
    {
        // Types ///////////////////////////////////////////////////////////////
        
        public enum Mode
        {
            HostLocal,
            HostRemote,
            ClientLocal,
        }

        public partial class PluginItem(LanguageSwitch language, PluginPackInfo info) : ObservableObject
        {
            public String ID => info.ID;

            public String Title
            {
                get
                {
                    String errorText = "";
                    switch (Error)
                    {
                        case PluginPackError.Disabled:
                            errorText = language["disabled"];
                            break;
                        case PluginPackError.LoadFailed:
                            errorText = language["load-failed"];
                            break;
                        case PluginPackError.Unlicensed:
                            errorText = language["unlicensed"];
                            break;
                        case PluginPackError.GUIUnsupported:
                            errorText = language["gui-unsupported"];
                            break;
                    }
                    if (errorText.Length > 0) errorText = " (" + errorText + ")";
                    return info.Name + errorText;
                }
            }

            [ObservableProperty]
            [NotifyPropertyChangedFor(nameof(Title))]
            private PluginPackError error = info.Error;
        }

        // Plugin list /////////////////////////////////////////////////////////

        public async void InitializeList()
        {
            var infos = new List<PluginPackInfo>();
            if (mode == Mode.HostRemote)
            {
                var pluginIDs = await AgencyAsync.GetPluginPackIDList();
                if (pluginIDs == null) return;

                var pluginInfos = await Task.WhenAll(new Task<PluginPackInfo>[pluginIDs.Length].Populate(i => AgencyAsync.GetPluginPackInfo(pluginIDs[i])));
                for (int i = 0; i < pluginIDs.Length; i++)
                {
                    if (pluginInfos[i] != null) infos.Add(pluginInfos[i]);
                }
            }
            else
            {
                foreach (var id in AgencyLocal.GetPluginPackIDList())
                {
                    var pack = AgencyLocal.GetPluginPackInfo(id);
                    if (pack != null) infos.Add(pack);
                }
            }

            infos.Sort((a, b) => a.Name.CompareTo(b.Name));

            foreach (var info in infos)
            {
                var item = new PluginItem(language, info);
                var description = info.ID + "\n" + info.Name + "\n" + info.Version.ToString() + "\n" + info.Brief + "\n" + info.AppLayerDetails + "\n" + info.NativeLayerDetails;
                pluginDescriptionTable[item] = description.ToLower();
                PluginItems.Add(item);
            }
        }

        private Dictionary<PluginItem, String> pluginDescriptionTable = new Dictionary<PluginItem, String>();

        [ObservableProperty]
        private ObservableCollection<PluginItem> pluginItems = new ObservableCollection<PluginItem>();

        [ObservableProperty]
        private PluginItem selectedPluginItem = null;

        partial void OnSelectedPluginItemChanged(PluginItem value)
        {
            SelectedPluginInfo = null;
            if (value == null)
            {
                SelectedPluginEnabled = false;
                return;
            }
            updateSelectedPluginInfo(value);
        }

        [ObservableProperty]
        private String searchText = "";

        partial void OnSearchTextChanged(string value)
        {
            var key = value != null && value.Length >= 2 ? value.ToLower() : null;
            PluginItems.Clear();
            foreach (var item in pluginDescriptionTable.Keys)
            {
                if (key != null && !pluginDescriptionTable[item].Contains(key)) continue;
                PluginItems.Add(item);
            }
        }

        [RelayCommand]
        private async Task EnableAllPlugins()
        {
            if (!await AgencyLocal.PopupConfirm(language["confirm-enable"])) return;

            if (mode == Mode.HostRemote)
            {
                var tasks = new List<Task>();
                foreach (var packID in (await AgencyAsync.GetPluginPackIDList()) ?? [])
                {
                    tasks.Add(AgencyAsync.EnablePlugin(packID));
                }
                await Task.WhenAll(tasks);
                Modified = true;
            }
            else
            {
                foreach (var packID in AgencyLocal.GetPluginPackIDList())
                {
                    AgencyLocal.EnablePlugin(packID);
                }
                await AgencyLocal.PopupNotice(language["restart-notice"]);
            }

            updateAfterAllEnabledOrDisabled();
        }

        [RelayCommand]
        private async Task DisableAllPlugins()
        {
            if (!await AgencyLocal.PopupConfirm(language["confirm-disable"])) return;

            if (mode == Mode.HostRemote)
            {
                await AgencyAsync.DisableAllPlugins();
            }
            else
            {
                AgencyLocal.DisableAllPlugins();
            }
            Modified = true;

            updateAfterAllEnabledOrDisabled();
        }

        [RelayCommand]
        private async Task InstallPlugin()
        {
            if (mode == Mode.HostRemote)
            {
                if (caller != null)
                {
                    await AgencyAsync.RunConsole(caller, "install-plugin.consoleworkflow");
                    Modified = true;
                }
            }
            else
            {
                var selected = await App.ShowSelectFolderDialog(view, language["install-title"]);
                if (selected != null)
                {
                    if (await AgencyLocal.InstallPlugin(selected))
                    {
                        var itemsToRemove = new List<PluginItem>();
                        foreach (var item in PluginItems)
                        {
                            if (AgencyLocal.GetPluginPackInfo(item.ID) == null) itemsToRemove.Add(item);
                        }
                        foreach (var item in itemsToRemove)
                        {
                            PluginItems.Remove(item);
                        }
                        Modified = true;
                    }
                }
            }
        }

        // Plugin details ///////////////////////////////////////////////////////

        [ObservableProperty]
        private bool selectedPluginEnabled = false;

        partial void OnSelectedPluginEnabledChanged(bool value)
        {
            if (SelectedPluginItem == null || SelectedPluginInfo == null || SelectedPluginItem.ID != SelectedPluginInfo.ID) return;

            if (mode == Mode.HostRemote)
            {
                if (value)
                {
                    AgencyAsync.EnablePlugin(SelectedPluginInfo.ID);
                }
                else
                {
                    AgencyAsync.DisablePlugin(SelectedPluginInfo.ID);
                    SelectedPluginItem.Error = PluginPackError.Disabled;
                    updateSelectedPluginInfo(SelectedPluginItem);
                }
            }
            else
            {
                if (value)
                {
                    AgencyLocal.EnablePlugin(SelectedPluginInfo.ID);
                }
                else
                {
                    AgencyLocal.DisablePlugin(SelectedPluginInfo.ID);
                    SelectedPluginItem.Error = PluginPackError.Disabled;
                    updateSelectedPluginInfo(SelectedPluginItem);
                }
            }
        }

        [ObservableProperty]
        [NotifyPropertyChangedFor(nameof(SelectedPluginEnableText))]
        [NotifyPropertyChangedFor(nameof(SelectedPluginVersion))]
        [NotifyPropertyChangedFor(nameof(SelectedPluginBrief))]
        [NotifyPropertyChangedFor(nameof(SelectedPluginAppLayerDetails))]
        [NotifyPropertyChangedFor(nameof(SelectedPluginNativeLayerDetails))]
        private PluginPackInfo selectedPluginInfo = null;

        private async void updateSelectedPluginInfo(PluginItem item)
        {
            PluginPackInfo info = null;
            if (mode == Mode.HostRemote)
            {
                info = await AgencyAsync.GetPluginPackInfo(item.ID);
            }
            else
            {
                info = AgencyLocal.GetPluginPackInfo(item.ID);
            }
            if (SelectedPluginItem == null || SelectedPluginItem.ID != item.ID) return;

            if (info != null)
            {
                SelectedPluginEnabled = info.Status == PluginPackStatus.Enabled || info.Status == PluginPackStatus.ToBeEnabled;
                SelectedPluginEnableText = info.Status == PluginPackStatus.Enabled ? language["enabled"] : language["to-be-enabled"];
                item.Error = info.Error;
            }
            else
            {
                SelectedPluginEnabled = false;
            }
            SelectedPluginInfo = info;
        }

        private async void updateAfterAllEnabledOrDisabled()
        {
            var items = PluginItems.ToArray();
            var infos = new PluginPackInfo[items.Length];
            if (mode == Mode.HostRemote)
            {
                var pluginInfos = await Task.WhenAll(new Task<PluginPackInfo>[items.Length].Populate(i => AgencyAsync.GetPluginPackInfo(items[i].ID)));
                for (int i = 0; i < items.Length; i++)
                {
                    if (pluginInfos[i] != null) items[i].Error = pluginInfos[i].Error;
                }
            }
            else
            {
                for (int i = 0; i < items.Length; i++)
                {
                    var pack = AgencyLocal.GetPluginPackInfo(items[i].ID);
                    if (pack != null) items[i].Error = pack.Error;
                }
            }

            if (SelectedPluginItem != null)
            {
                updateSelectedPluginInfo(SelectedPluginItem);
            }
        }

        [ObservableProperty]
        private String selectedPluginEnableText = language["enabled"];

        public String SelectedPluginVersion => SelectedPluginInfo?.Version.ToString();

        public String SelectedPluginBrief => SelectedPluginInfo?.Brief;

        public String SelectedPluginAppLayerDetails => SelectedPluginInfo?.AppLayerDetails;

        public String SelectedPluginNativeLayerDetails => SelectedPluginInfo?.NativeLayerDetails;

        public bool CanUninstall => mode != Mode.HostRemote;

        [RelayCommand]
        private async Task Uninstall()
        {
            var item = SelectedPluginItem;
            if (!await AgencyLocal.PopupConfirm(language.Format("confirm-uninstall", item.Title))) return;

            if (AgencyLocal.UninstallPlugin(item.ID))
            {
                PluginItems.Remove(item);
                Modified = true;
            }
        }

        // Result //////////////////////////////////////////////////////////////

        public bool Modified { get; private set; } = false;
    }
}